////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file   CollabRCBotUnitTests\TestWordChoiceGene.h
///
/// @brief  Defines the test suite for CollabRC::Bot::AI::WordChoiceGene
////////////////////////////////////////////////////////////////////////////////////////////////////

#include <cxxtest/TestSuite.h>
#include "../CollabRCBot/WordChoiceGene.h"
#include <QtCore/QString>
#include <QtCore/QSet>
#include "MockWordSet.h"
#include "MockDiff.h"
#include "RandomMock.h"
#include "../CollabRCBot/OperationTreeNodeImpl.h"
#include "../CollabRCBot/MemberVariableTreeNode.h"

class TestWordChoiceGene : public CxxTest::TestSuite
{
private:
    /**
     * Harness for CollabRC::Bot::AI::WordChoiceGene that exposes some members
     * for testing
     */
    class WordChoiceGeneHarness : public CollabRC::Bot::AI::WordChoiceGene
    {
    public:
        using WordChoiceGene::ComputeWeightedSum;
        using WordChoiceGene::ComputeWordPresence;
        WordChoiceGeneHarness(const CollabRC::Bot::WordSet& set):WordChoiceGene(set) { };
    };

    /**
     * Test harness for CollabRC::Bot::AI::WordChoiceGene that overrides the default
     * ComputeWeightedSum and ComputeWordPresence to test the use of the algorithm for
     * combination of the two parameters
     */
    class WordChoiceGeneComputationHarness : public CollabRC::Bot::AI::WordChoiceGene
    {
    private:
        float m_presence, m_sum;

    public:
        WordChoiceGeneComputationHarness(float presence, float sum)
            :WordChoiceGene(CreateDefaultAlgorithm<CollabRC::Bot::AI::AdditionTreeNode>()),
             m_presence(presence),m_sum(sum) { };

        WordChoiceGeneComputationHarness(const WordChoiceGeneComputationHarness& copy):
            WordChoiceGene(copy),m_presence(copy.m_presence),m_sum(copy.m_sum) { }

        inline void SetValues(float presence, float sum)
        {
            m_presence = presence;
            m_sum = sum;
        }

    protected:
        virtual float ComputeWeightedSum(const CollabRC::Bot::Diff& diff) const { return m_sum; };
        virtual float ComputeWordPresence(const CollabRC::Bot::Diff& diff) const { return m_presence; };
    };

    class MockVisitor : public CollabRC::Bot::AI::ComputationalTreeNodeVisitor
    {
    private:
        bool m_visited;
    public:
        MockVisitor():m_visited(false) { };
        virtual void Visit(CollabRC::Bot::AI::MemberVariableTreeNode<CollabRC::Bot::AI::WordChoiceGene, float>& node)
        {
            m_visited = true;
        }

        bool WasVisited() const { return m_visited; };
    };
public:
    void TestEmptySet()
    {
        MinRandom rand;

        MockWordSet set;
        MockDiff diff(set, set);
        WordChoiceGeneHarness gene(set);

        // Word presence and weighted sums should be zero
        TS_ASSERT_EQUALS(gene.ComputeWeightedSum(diff), 0);
        TS_ASSERT_EQUALS(gene.ComputeWordPresence(diff), 0);

        // Try with words, it should still be zero
        MockWordSet set2;
        set2.AddWord("Hello, world");
        MockDiff diff2(set2, set2);
        TS_ASSERT_EQUALS(gene.ComputeWeightedSum(diff2), 0);
        TS_ASSERT_EQUALS(gene.ComputeWordPresence(diff2), 0);
    }

    void TestFilledSet()
    {
        MinRandom rand;

        MockWordSet wordList;
        wordList.AddWord("Word1");
        wordList.AddWord("Word2");

        MockWordSet diffAdded, diffRemoved;
        diffAdded.AddWord("Word1");
        diffAdded.AddWord("Word3");
        diffAdded.AddWord("Word4");
        MockDiff diff(diffAdded, diffRemoved);

        WordChoiceGeneHarness gene(wordList);
        TS_ASSERT_EQUALS(gene.ComputeWeightedSum(diff), 1);
        TS_ASSERT_EQUALS(gene.ComputeWordPresence(diff), 0.5f);
    }

    void TestComputation()
    {
        MockWordSet set;
        MockDiff diff(set, set);
        WordChoiceGeneComputationHarness gene(2.25f, 10.0f);
        TS_ASSERT_EQUALS(gene.Test(diff), 12.25f);

        // Try changing the values
        gene.SetValues(0.0f, 0.0f);
        TS_ASSERT_EQUALS(gene.Test(diff), 0.0f);

        // Try copying
        WordChoiceGeneComputationHarness copy(gene);
        TS_ASSERT_EQUALS(copy.Test(diff), 0.0f);

        // Try changing the new one
        copy.SetValues(2.25f, 10.0f);
        TS_ASSERT_EQUALS(gene.Test(diff), 0.0f);
        TS_ASSERT_EQUALS(copy.Test(diff), 12.25f);
    }
};